package com.youboy.wechatapi.web.controller;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.type.JavaType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.youboy.databaseapi.api.CompanyService;
import com.youboy.databaseapi.domain.Company;
import com.youboy.wechatapi.domain.enums.BindResult;
import com.youboy.wechatapi.domain.enums.BindType;
import com.youboy.wechatapi.domain.mysql.BindInfo;
import com.youboy.wechatapi.service.bind.BindService;
import com.youboy.wechatapi.utils.http.HttpClient;
import com.youboy.wechatapi.utils.http.URLUtil;
import com.youboy.wechatapi.utils.json.JacksonMapper;
import com.youboy.wechatapi.utils.wechat.WechatApi;
import com.youboy.wechatapi.web.support.RegMsgUtil;

@Controller
public class BindController {

	private Logger log = Logger.getLogger(BindController.class);

	//微信用户授权获取用户信息接口
	@Value(value = "${api.oauth2.accesstoken}")
	private String api_oauth2_accesstoken;

	//微信接口开发者标识
	@Value(value = "${api.appid}")
	private String appid;
	@Value(value = "${api.secret}")
	private String secret;

	//注册接口
	@Value(value = "${reg.url}")
	private String regUrl;
	
	//注册验证接口
	@Value(value = "${reg.check.url}")
	private String regCheckUrl;
	
	//微商铺接口
	@Value(value = "${wechat.shop.url}")
	private String wechatShopUrl;
	
	@Value(value = "${wechatapi.url}")
	private String wechatapiUrl;

	@Autowired
	private BindService bindService;
	
	@Autowired
	private CompanyService dbCompanyService;
	
	/**
	 * 微信方回调接口，前往绑定页面
	 * @param model
	 * @param code
	 * 				code作为换取access_token的票据，每次用户授权带上的code将不一样，code只能使用一次，5
	 *            	分钟未被使用自动过期。
	 * @param state
	 * @return
	 */
	@RequestMapping(value = "/bind/index", produces = { "text/html;charset=utf-8" })
	public String indexNum(Model model, HttpSession session,
			@RequestParam(value = "code", required = false) String code,
			@RequestParam(value = "state") Integer state) {

		String openid = WechatApi.getOpenid(code);
		if(StringUtils.isBlank(openid) && state != null && state < 3){
			String url = null;
			int nextState = state + 1;
			
	    	try {
	    		url = WechatApi.OAUTH2_AUTHORIZE_URL.replace("APPID", WechatApi.APPID)
	    				.replace("REDIRECT_URI", URLEncoder.encode(WechatApi.URL+"/bind/index", "utf-8"))
	    				.replace("SCOPE", "snsapi_base").replace("STATE", nextState+"");
	    		log.info("[code:" + code + "][state:" + state + "] redirect oauth url, the state is " + nextState + " !");
			} catch (UnsupportedEncodingException e) {
				log.error("[code:" + code + "][state:" + state + "] get oauth2 url error !", e);
			}
	    	
	    	if(StringUtils.isBlank(url)){
	    		return "error";
	    	}
			return "redirect:" + url;
		}
		return this.getIndex(model, session, openid);
	}

	/**
	 * 前往绑定页面
	 * @param model
	 * @param session
	 * @return
	 */
	@RequestMapping(value = "/bind/toIndex", method = RequestMethod.GET, produces = { "text/html;charset=utf-8" })
	public String toBind(Model model, HttpSession session, @RequestParam(value = "openid") String openid) {
		
		return this.getIndex(model, session, openid);
	}
	
	
	private String getIndex(Model model, HttpSession session, String openid){
		if(StringUtils.isBlank(openid)){
			Object openObj = session.getAttribute("openid");
			
			if(openObj == null){
				model.addAttribute("bindResult", "不能进行绑定");
				model.addAttribute("bindMsg", "未能获取您的用户信息，不能进行商铺绑定，请尝试重新绑定！");
				log.error("[openid:" + openid + "] get openid fatal !");
				return "bind/bind-result";
			}
			openid = openObj.toString();
		}
		
		if(StringUtils.isBlank(openid)){
			log.error("[openid:" + openid + "] openid is null  !");
			return "error";
		}
	
		BindInfo bind = null;
		try {
			bind = bindService.getBindInfo(openid);
		} catch (Exception e) {
			log.error("[openid:" + openid + "] get bindInfo by openid error !", e);
		}

		if (bind != null) {
			Company company = dbCompanyService.getByCompanyUserName(bind.getUsername());
			if(company != null && company.getStatus() != 0){
				log.info("[openid:" + openid + "][us:" + bind.getUsername() + "] company info is not approved, can't to shop !");
				return "company-error";
			}
			
			String url = null;
			String wechatShop = wechatShopUrl.replace("USERNAME", bind.getUsername());
			try {
				url = this.setCallBackUrl(openid, wechatShop);
				log.info("[openid:" + openid + "] get bindInfo by openid success , redirect to wechat shop !");
			} catch (UnsupportedEncodingException e) {
				url = wechatShop;
				log.error("[openid:" + openid + "] get callBack url error!", e);
			}
			return "redirect:" + url;
		}

		session.setAttribute("openid", openid);
		model.addAttribute("openid", openid);
		
		//return "bind";
		return "bind/bind";
	}
	
	private String setCallBackUrl(String openid, String redirectUrl) throws UnsupportedEncodingException{
		StringBuilder url = new StringBuilder();
		url.append(wechatapiUrl).append("/help/callBack?openid=").append(openid)
				.append("&redirect_url=").append(URLEncoder.encode(redirectUrl, "utf-8"));
		return url.toString();
	}
	
	/**
	 * 提交绑定申请
	 * 
	 * @param model
	 * @param us
	 * @param pwd
	 * @param openid
	 * @return
	 */
	@RequestMapping(value = "/bind/submit", method = RequestMethod.POST, produces = { "text/html;charset=utf-8" })
	public String submit(Model model, HttpSession session, @RequestParam(value = "us") String us,
			@RequestParam(value = "pwd") String pwd, @RequestParam(value = "openid") String openid) {
		
		if(StringUtils.isBlank(openid)){
			Object openObj = session.getAttribute("openid");
			
			if(openObj == null){
				this.setBindMsg(model, false, "绑定失败", "未能获取您的用户信息，不能进行商铺绑定，请尝试重新绑定或联系客服！");
				log.error("[us:" + us + "][openid:" + openid + "] can't get openid, bind fail  !");
				return "bind/bind-result";
			}
			openid = openObj.toString();
		}
		
		session.setAttribute("username", us);
		session.setAttribute("openid", openid);
		model.addAttribute("openid", openid);
		
		us = us.replace(" ", "");
		
		if(StringUtils.isBlank(us)){
			this.setBindMsg(model, false, "绑定失败", "请填写正确的用户名！");
			log.warn("[us:" + us + "][openid:" + openid + "] username is null, bind fail !");
			return "bind/bind-result";
		}
		
		Company company = dbCompanyService.getByCompanyUserName(us);
		if(company == null){
			this.setBindMsg(model, false, "绑定失败", "绑定的商铺不存在！");
			log.info("[openid:" + openid + "][us:" + us + "] company info is null, can't bind !");
			return "bind/bind-result";
		}
		
		BindResult bindResult = saveBindInfo(openid, us, BindType.JUST);
		if (bindResult == BindResult.EXIST) {
			this.setBindMsg(model, false, "绑定失败", "已有您的绑定数据，不能进行重复绑定！");
			log.info("[us:" + us + "][openid:" + openid + "] Data already exists, bind fail !");
			return "bind-result";
		} else if(bindResult == BindResult.FAIL){
			this.setBindMsg(model, false, "绑定失败", "绑定失败，请重新绑定或拨打下方电话联系客服！");
			log.error("[us:" + us + "][openid:" + openid + "] bind fail !");
			return "bind/bind-result";
		}
		
		this.setBindMsg(model, true, "绑定成功", "已成功绑定商铺！");
		return "bind/bind-result";
	}
	
	private void setBindMsg(Model model,boolean result, String title, String msg){
		Map<String, Object> bindResult = new HashMap<String, Object>();
		bindResult.put("result", result);
		bindResult.put("title", title);
		bindResult.put("msg", msg);
		model.addAttribute("bindResult", bindResult);
	}
	
	
	/**
	 * 序列化绑定信息
	 */
	private BindResult saveBindInfo(String openid, String us, BindType type){
		BindResult bindResult = BindResult.FAIL;
		try {
			bindService.save(openid, us.trim().toLowerCase(), type);
			bindResult = BindResult.SUCCESS;
			log.info("[us:" + us + "][openid:" + openid + "] save bindInfo success !");
		} catch (Exception e) {
			if( e.getMessage().contains("Duplicate entry") ){
				bindResult = BindResult.EXIST;
			}
			log.error("[us:" + us + "][openid:" + openid + "] save bindInfo error !", e);
		}
		return bindResult;
	}

	/**
	 * 前往注册页面
	 */
	@RequestMapping(value = "/bind/toReg", method = RequestMethod.GET, produces = { "text/html;charset=utf-8" })
	public String goReg(Model model, HttpSession session, HttpServletResponse response, 
			@RequestParam(value = "openid", required = false) String openid) {

		if(StringUtils.isBlank(openid)){
			Object openObj = session.getAttribute("openid");
			
			/*if(openObj == null){
				model.addAttribute("regResult", "不能进行注册");
				model.addAttribute("regMsg", "未能获取您的用户信息，不能进行商铺注册，请尝试重新绑定！");
				log.error("[openid:" + openid + "] bind fail  !");
				return "bind-fail";
			}*/
			openid = openObj.toString();
		}
		
		Object token = session.getAttribute("token");
		
		if (token == null) {
			token = (new Random().nextInt(8999) + 1000) + "";
			session.setAttribute("token", token);
		}
		session.setAttribute("openid", openid);
		model.addAttribute("openid", openid);
		
		return "reg/reg";
	}
	
	/**
	 * 微信注册
	 * 
	 * @param model
	 * @param request
	 * @return
	 */
	@SuppressWarnings({ "unchecked" })
	@RequestMapping(value = "/bind/ajaxReg", method = RequestMethod.POST, produces = { "text/html;charset=utf-8" })
	@ResponseBody
	public String ajaxReg(HttpSession sesstion, HttpServletRequest request) {

		Map<String, String[]> requestMap = request.getParameterMap();
		Map<String, Object> paramsMap = new HashMap<String, Object>();
		StringBuilder logStr = new StringBuilder();
		
		for (String key : requestMap.keySet()) {
			Object obj = requestMap.get(key)[0];
			paramsMap.put(key, obj);
			logStr.append("[").append(key).append(":").append(obj).append("]");
		}
		
		String ip = request.getHeader("ipip");
		if (ip == null) {
			ip = request.getRemoteAddr();
		}
		paramsMap.put("ip", ip);
		logStr.append("[").append("ip").append(":").append(ip).append("]");
		 
		//将map转换成url参数
		String params = null;
		try {
			params = URLUtil.getUrlParams(paramsMap);
		} catch (UnsupportedEncodingException e) {
			log.error(logStr + " getUrlParams error !", e);
		}

		String regResult = null;
		try {
			regResult = HttpClient.httpRequest(regUrl, params);
		} catch (IOException e) {
			log.error(logStr + " send reg info to my.youboy.com error!", e);
		}

		Map<String, Object> resultMap = null;
		if (StringUtils.isBlank(regResult)) {
			log.error(logStr + "get reg result is null !");
			return getResultJson(1, "RESULT_NULL", "没有结果，注册失败。");
		}
		
		JavaType type = JacksonMapper.getInstance().getTypeFactory()
				.constructParametricType(Map.class, String.class, String.class);
		try {
			resultMap = JacksonMapper.getInstance().readValue(regResult, type);
		} catch (Exception e) {
			log.error(logStr + "get reg result convert to map error!", e);
		}
		
		if(resultMap == null || resultMap.size() == 0 
				|| resultMap.get("result") == null
				|| resultMap.get("result").equals("")){
			log.error(logStr + " resultMap is null , can't bind !");
			return getResultJson(1, "REG_RESULT_NULL", "无法获取注册结果，注册失败！");
		}
		
		Integer result = Integer.valueOf(resultMap.get("result").toString());
		if(result != 0){
			log.error(logStr + getResultStr(resultMap) + " wechat reg fail !");
			return getResultJson(result, resultMap.get("key").toString(), RegMsgUtil.regMsgMap.get(resultMap.get("key")));
		}
		
		Object openInfo = sesstion.getAttribute("openid");
		if(openInfo == null){
			log.error(logStr + " can't get openid from session, bind is don't continue !");
			return getResultJson(-1, "OPENID_NULL", "注册成功，但无法获取用户信息，导致绑定失败，请手动进行绑定！");
		}
		String us = paramsMap.get("n_user").toString();
		BindResult bindResult = saveBindInfo(openInfo.toString(), us, BindType.REG);
		if( bindResult == BindResult.SUCCESS){
			sesstion.setAttribute("username", paramsMap.get("n_user"));
			return getResultJson(0, "BIND_SUCCESS", "已成功注册并绑定商铺！");
		}else if(bindResult == BindResult.EXIST){
			return getResultJson(-3, "BIND_EXIST", "您已经绑定过了！");
		} else {
			return getResultJson(-2, "BIND_FAIL", "已成功注册但绑定商铺失败了。");
		}
	}
	
	
	/**
	 * 注册数据检测
	 * @param request
	 * @param response
	 * @return
	 */
	@SuppressWarnings({ "unchecked" })
	@RequestMapping(value = "/bind/regCheck")
	@ResponseBody
	public String checkall(HttpServletRequest request) {
		
		Map<String, String[]> parameterMap = request.getParameterMap();
		Map<String, Object> regParams = new HashMap<String, Object>();
		StringBuilder logStr = new StringBuilder();

		for (String key : parameterMap.keySet()) {
			Object obj = parameterMap.get(key)[0];
			regParams.put(key, obj);
			logStr.append("[").append(key).append(":").append(obj).append("]");
		}

		//将map转换成url参数
		String paramsStr = null;
		try {
			paramsStr = URLUtil.getUrlParams(regParams);
		} catch (UnsupportedEncodingException e) {
			log.error(logStr + " getUrlParams error !", e);
		}
		
		String regResult = null;
		try {
			regResult = HttpClient.httpRequest(regCheckUrl, paramsStr);
			log.info(logStr + "send reg checkinfo, return info is " + regResult);
		} catch (IOException e) {
			log.error(logStr + " send reg check info to my.youboy.com error!", e);
		}
		return regResult;
	}
	
	@RequestMapping(value = "/bind/regSuccess", method = RequestMethod.GET, produces = { "text/html;charset=utf-8" })
	public String regSuccess(Model model,
			@RequestParam(value = "result", required = false) int result, 
			@RequestParam(value = "key", required = false) String key, 
			@RequestParam(value = "msg", required = false) String msg) {
		
		Map<String, Object> regResult = new HashMap<String, Object>();
		regResult.put("result", result);
		regResult.put("key", key);
		regResult.put("msg", msg);
		
		model.addAttribute("regResult", regResult);
		return "reg/reg-result";
	}
	
	
	/**
	 * 获取注册用返回json
	 */
	private String getResultJson(int result, String key, String msg){
		StringBuilder jsonResult = new StringBuilder();
		jsonResult.append("{\"result\":\"").append(result).append("\",\"key\":\"")
			.append(key).append("\",\"msg\":\"").append(msg).append("\"}");
		return jsonResult.toString();
	}
	
	private String getResultStr(Map<String, Object> resultMap){
		StringBuilder resultStr = new StringBuilder();
		for (String result : resultMap.keySet()) {
			resultStr.append("[").append(result).append(":").append(resultMap.get(result)).append("]");
		}
		return resultStr.toString();
	}
	
	/**
	 * 查看youboy服务条款
	 */
	@RequestMapping(value = "/bind/service", produces = { "text/html;charset=utf-8" })
	public String toService(Model model) {
		return "reg/service";
	}

}
